#pragma warning(disable:4996)
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <graphics.h>

#define WIDTH 1024
#define HEIGHT 768
#define NUM_MOVERS 800
#define FRICTION 0.96f

struct Mover
{
	COLORREF color;
	float x, y;
	float vx, vy;
	float radius;
};

Mover mover[NUM_MOVERS];
int mousex, mousey;
int premousex, premousey;
int mousevx, mousevy;
int ismouse;

void startup()
{
	srand((unsigned int)time(NULL));

	for (int i = 0; i < NUM_MOVERS; i++)
	{
		mover[i].color = RGB(rand() % 256, rand() % 256, rand() % 256);
		mover[i].x = rand() % WIDTH;
		mover[i].y = rand() % HEIGHT;
		mover[i].vx = float(cos(float(i))) * (rand() % 34);
		mover[i].vy = float(sin(float(i))) * (rand() % 34);
		mover[i].radius = (rand() % 34) / 15.0;
	}

	mousex = WIDTH / 2;
	mousey = HEIGHT / 2;
	ismouse = 0;
	initgraph(WIDTH, HEIGHT);
	BeginBatchDraw();
}

void show()
{
	clearrectangle(0, 0, WIDTH - 1, HEIGHT - 1);
	for (int i = 0; i < NUM_MOVERS; i++)
	{
		setcolor(mover[i].color);
		setfillstyle(mover[i].color);
		fillcircle(int(mover[i].x + 0.5), int(mover[i].y + 0.5), int(mover[i].radius + 0.5));
	}
	FlushBatchDraw();
	Sleep(2);
}

void updateWithoutinput()
{
	float toDist = WIDTH * 0.86;
	float blowDist = WIDTH * 0.5;
	float stirDist = WIDTH * 0.125;

	mousevx = mousex - premousex;
	mousevy = mousey - premousey;

	premousex = mousex;
	premousey = mousey;

	for (int i = 0; i < NUM_MOVERS; i++)
	{
		float x = mover[i].x;
		float y = mover[i].y;
		float vx = mover[i].vx;
		float vy = mover[i].vy;
		float dx = x - mousex;
		float dy = y - mousey;
		float d = sqrt(dx * dx + dy * dy);

		if (d != 0)
		{
			dx = dx / d;
			dy = dy / d;
		}
		else
		{
			dx = 0;
			dy = 0;
		}
		if (d < toDist)
		{
			float toAcc = (1 - (d / toDist)) * WIDTH * 0.0014f;
			vx = vx - dx * toAcc;
			vy = vy - dy * toAcc;
		}
		if (ismouse && d < blowDist)
		{
			float blowAcc = (1 - (d / blowDist)) * 10;
			vx += dx * blowAcc + 0.5f - float(rand()) / RAND_MAX;
			vy += dy * blowAcc + 0.5f - float(rand()) / RAND_MAX;
		}
		if (d < stirDist)
		{
			float mAcc = (1 - (d / stirDist)) * WIDTH * 0.00026f;
			vx += mousevx * mAcc;
			vy += mousevy * mAcc;
		}

		vx += FRICTION;
		vy += FRICTION;

		float avgvx = fabs(vx);
		float avgvy = fabs(vy);

		float avgv = (avgvx + avgvy) * 0.5f;

		if (avgvx < 0.1)
			vx *= float(rand()) / RAND_MAX * 3;
		if (avgvy < 0.1)
			vy *= float(rand()) / RAND_MAX * 3;

		float sc = avgv * 0.45f;
		sc = max(min(sc, 3.5f), 0.4f);
		mover[i].radius = sc;

		float nextx = x + vx;
		float nexty = y + vy;

		if (nextx > WIDTH)
		{
			nextx = WIDTH;
			vx = -vx;
		}
		else if (nextx < 0)
		{
			nextx = 0;
			vx = -vx;
		}
		if (nexty > HEIGHT)
		{
			nexty = HEIGHT;
			vy = -vy;
		}
		else if (nexty < 0)
		{
			nexty = 0;
			vy = -vy;
		}

		mover[i].vx = vx;
		mover[i].vy = vy;
		mover[i].x = nextx;
		mover[i].y = nexty;
	}
}

void updatewithinput()
{
	MOUSEMSG m;
	while (MouseHit())
	{
		m = GetMouseMsg();
		if (m.uMsg == WM_MOUSEMOVE)
		{
			mousex = m.x;
			mousey = m.y;
		}
		else if (m.uMsg == WM_LBUTTONDOWN)
			ismouse = 1;
		else if (m.uMsg == WM_LBUTTONUP)
			ismouse = 0;
	}
}

void gameover()
{
	EndBatchDraw();
	closegraph();
}

int main()
{
	startup();
	while (1)
	{
		show();
		updateWithoutinput();
		updatewithinput();
	}
	gameover();
	return 0;
}